<?php

namespace app\logic;

use app\constant\CommonConstant;
use app\constant\ModelConstant;
use app\exception\BusinessException;
use app\exception\PaypalBusinessException;
use app\libs\PaypalSDK\action\PurchasePaypal;
use app\libs\PaypalSDK\builder\PrePurchaseBuilder;
use app\model\Address;
use app\model\LineItems;
use app\model\Notify;
use app\model\Orders;
use app\model\ShopsPayment;
use app\queue\CreateDraftOrderQueue;
use app\service\payment\gateway\PaymentBase;
use app\service\payment\gateway\PaypalService;
use app\service\payment\gateway\SpreedlyService;
use app\service\payment\PaymentFactory;
use app\service\shopify\action\graph\DeliveryGraph;
use app\service\shopify\action\graph\OrderGraph;
use app\service\shopify\action\rest\DraftOrderRest;
use app\service\shopify\action\rest\OrderRest;
use app\trait\OrderTrait;
use app\trait\PaymentTrait;
use app\trait\ShopifyTrait;
use Carbon\Carbon;
use think\Exception;
use think\facade\Cache;
use think\facade\Queue;
use think\helper\Arr;
use think\Request;

class OrderLogic
{
    use ShopifyTrait, OrderTrait;

    /**
     * @param Request $request
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     * @desc 创建草稿订单
     */
    public function createDraftOrder(Request $request)
    {
        $order = null;
        try {
            //存订单
            $order = $this->saveOrder($request);
            Queue::push(CreateDraftOrderQueue::class, $order->id,CreateDraftOrderQueue::QUEUE_NAME);
            return ['order_id'=>$order->id];
        } catch (\Exception $e) {
            if ($order) Orders::saveExceptionMessage($order, $e->getMessage());
            throw new BusinessException($e->getMessage());
        }
    }


    /**
     * @param Request $request
     * @param Orders $order
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     * @desc 修改草稿订单
     */
    public function updateDraftOrder(Request $request, Orders $order)
    {
        try {
            $this->saveOrder($request, $order);
            Queue::push(CreateDraftOrderQueue::class, $order->id,CreateDraftOrderQueue::QUEUE_NAME);
            return ['order_id'=>$order->id];
        } catch (\Exception $e) {
            Orders::saveExceptionMessage($order, $e->getMessage());
        }
    }

    public function placeOrder(Request $request, Orders $order)
    {
        $payment = ShopsPayment::find($request->param('payment_id', 0));
        if (is_null($payment)) throw new BusinessException("receive payee not exists");
        $order->pay_id = $payment->id;
        $order->pay_method = $payment->pay_method;
        $order->save();
        $gateway = PaymentFactory::factory($payment->pay_method);
        return $gateway->createOrder($order, $request);
    }


    public function getShippingZones(Request $request)
    {
        try {
            $cacheKey = sprintf("%s.deliver.list",$request->header('X-Opc-Shop-Id'));
            $redis = Cache::store("redis");
            if($redis->has($cacheKey)){
                $shipping_fee_list = json_decode($redis->get($cacheKey), true);
            }else {
                //采用缓存获取物流
                $currency_rate = $request->param('currency_rate', '1.00');
                $countryCode = $request->param('country_code');
                $countryCode = strtoupper($countryCode);
                $shipping_fee_list = [];
                $subTotal = $request->param('sub_total', 0);
                $graph = new DeliveryGraph($request->middleware('x_shop_id'), true);
                $result = $graph->getDeliveryProfiles();
                $data = Arr::get($result, 'data.deliveryProfiles.edges');
                foreach ($data as $item) {
                    $profileLocationGroups = Arr::get($item, 'node.profileLocationGroups', []);
                    if (empty($profileLocationGroups)) continue;
                    foreach ($profileLocationGroups as $profileLocationGroup) {
                        $locationGroupZones = Arr::get($profileLocationGroup, 'locationGroupZones.edges', []);
                        if (empty($locationGroupZones)) continue;
                        foreach ($locationGroupZones as $locationGroupZone) {
                            $countries = Arr::get($locationGroupZone, 'node.zone.countries', []);
                            $methodDefinitions = Arr::get($locationGroupZone, 'node.methodDefinitions.edges', []);
                            $countryCodes = [];
                            $shippingItemLines = [];
                            if ($countries) {
                                foreach ($countries as $country) {
                                    $countryCodes[] = Arr::get($country, 'code.countryCode');
                                }
                            }
                            if ($methodDefinitions) {
                                foreach ($methodDefinitions as $methodDefinition) {
                                    $methodConditions = Arr::get($methodDefinition, 'node.methodConditions', []);
                                    $methodConditionFirst = Arr::first($methodConditions);
                                    $operation = Arr::get($methodConditionFirst, 'operator');
                                    $amount = Arr::get($methodConditionFirst, 'conditionCriteria.amount');
                                    $range = $operation == 'LESS_THAN_OR_EQUAL_TO' ? [0, $amount] : [$amount, 9999999];
                                    $shippingItemLines[] = [
                                        'title' => Arr::get($methodDefinition, 'node.name'),
                                        'price' => Arr::get($methodDefinition, 'node.rateProvider.price.amount'),
                                        'range' => $range,
                                    ];
                                }
                            }
                            $shipping_fee_list[] = [
                                'country_codes' => $countryCodes,
                                "shipping_line" => $shippingItemLines
                            ];
                        }
                    }
                }
                $redis->set($cacheKey, json_encode($shipping_fee_list), 60 * 60 * 2);

            }

            $shipping_lines = [];
            $default_shipping_line = [
                'title' => "Free",
                'price' => '0.00',
                'origin_price' => '0.00',
                'handle' => null,
                'custom' => true
            ];

            if (empty($shipping_fee_list)) return [$default_shipping_line];
            $delivery_country_shipping_line = [];
            foreach ($shipping_fee_list as $key => $line) {
                $countryCodes = Arr::get($line, 'country_codes', []);
                if (in_array($countryCode, $countryCodes)) {
                    $delivery_country_shipping_line = Arr::get($line, 'shipping_line', []);
                    break;
                }
            }
            if (empty($delivery_country_shipping_line)) return [$default_shipping_line];
            foreach ($delivery_country_shipping_line as $line) {
                $range = Arr::get($line, 'range', []);
                $min_order_subtotal = Arr::first($range);
                $max_order_subtotal = Arr::last($range);
                $price = Arr::get($line, 'price');
                $shippingTitle = Arr::get($line, 'title');
                if (!$min_order_subtotal && !$max_order_subtotal) {
                    $shipping_lines[] = [
                        'title' => $shippingTitle,
                        'price' => bcmul((string)$price, $currency_rate, 2),
                        'origin_price' => $price,
                        'handle' => null,
                        'custom' => true
                    ];
                } else {
                    //汇率计算
                    $min_order_subtotal_currency = bcmul((string)$min_order_subtotal, $currency_rate, 2);
                    $max_order_subtotal_currency = bcmul((string)$max_order_subtotal, $currency_rate, 2);
                    if ($subTotal <= $max_order_subtotal_currency && $subTotal >= $min_order_subtotal_currency) {
                        $shipping_lines[] = [
                            'title' => $shippingTitle,
                            'price' => bcmul((string)$price, $currency_rate, 2),
                            'origin_price' => $price,
                            'handle' => null,
                            'custom' => true
                        ];
                    }
                }
            }
            return array_reverse($shipping_lines);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * @param Request $request
     * @return array|void
     * @desc 快捷支付
     */
    public function quickPay(Request $request)
    {
        $x_shop_id = $request->x_shop_id;
        $paypal = ShopsPayment::query()->where('shop_id', $x_shop_id)->where('pay_method', ShopsPayment::PAY_METHOD_PAYPAL)->findOrEmpty();
        if ($paypal->isEmpty()) throw new \Exception('paypal no setting');
        $order = $this->saveOrder($request);
        $order->pay_id = $paypal->id;
        $order->pay_method = ShopsPayment::PAY_METHOD_PAYPAL;
        $order->is_paypal_kj = ModelConstant::STATUS_ON;
        $order->paypal_method = $request->param('paypal_method', 0);
        $order->save();
        $service = new PaypalService();
        return $service->quickPay($request, $order);
    }

    public function getPaypalConfig(Request $request)
    {
        $payment = ShopsPayment::payment($request->middleware('x_shop_id'), ShopsPayment::PAY_METHOD_PAYPAL);
        if (empty($payment)) throw new \Exception('paypal no setting');
        $config = $payment->config;

        return [
            'mode' => Arr::get($config, 'env', ''),
            'client_id' => Arr::get($config, 'app_key', ''),
        ];
    }

    protected function formatDraftInput(Request $request, array $address = []): array
    {
        $checkout = $request->param('checkout', []);
        $shipping_line = $request->param('shipping_line', []);
        $shipping_address = $request->param('shipping_address', []);
        if ($address) $shipping_address = $address;
        $billing_address = $request->param('billing_address', []);
        $lineItems = Arr::get($checkout, 'cart.items', []);
        $currencyCode = Arr::get($checkout, 'cart.currency', 'USD');
        $lines = array_map(function ($item) use ($currencyCode) {
            $variantId = sprintf("gid://shopify/ProductVariant/%s", Arr::get($item, 'id'));
            return [
                "priceOverride" => [
                    "amount" => bcdiv(Arr::get($item, "price"), '100', 2),
                    "currencyCode" => $currencyCode
                ],
                "components" => [
                    "quantity" => Arr::get($item, "quantity", 1),
                    'variantId' => $variantId
                ],
                "quantity" => Arr::get($item, "quantity", 1),
                "variantId" => $variantId,
                "customAttributes" => [
                    [
                        'key' => 'sku',
                        'value' => Arr::get($item, 'sku','') ?: Arr::get($item, 'key')
                    ]
                ],

            ];
        }, $lineItems);
        $paypal_method = $request->param('paypal_method', 0);
        $tag = match ($paypal_method) {
            Orders::PAYPAL_METHOD_GOOGLE => "googlePay",
            Orders::PAYPAL_METHOD_APPLE => "applePay",
            default => "Card"
        };
        $input = [
            "customAttributes" => [
                ['key' => "ip", 'value' => $request->ip()],
                ['key' => "ua", 'value' => $request->header('user-Agent')],
            ],
            'discountCodes' => Arr::get($checkout, 'discount_codes', []),
            "presentmentCurrencyCode" => $currencyCode,
            "tags" => [$tag],
            "lineItems" => $lines
        ];
        if ($email = $request->param('email')) $input['email'] = $email;
        if ($phone = Arr::get($shipping_address, 'phone')) $input['phone'] = $phone;
        if ($shipping_line) {
            $input['shippingLine'] = [
                "title" => Arr::get($shipping_line, 'title'),
                "price" => Arr::get($shipping_line, 'price')
            ];
        }
        if ($shipping_address) {
            $input['shippingAddress'] = [
                "address1" => Arr::get($shipping_address, "address1"),
                "city" => Arr::get($shipping_address, "city"),
                "province" => Arr::get($shipping_address, "province"),
                "country" => Arr::get($shipping_address, "country"),
                "zip" => Arr::get($shipping_address, "zip"),
                'firstName' => Arr::get($shipping_address, 'first_name'),
                'lastName' => Arr::get($shipping_address, 'last_name'),
            ];
            if ($address2 = Arr::get($shipping_address, 'address2', '')) {
                $input['shippingAddress']['address2'] = $address2;
            }
            if ($phone = Arr::get($shipping_address, 'phone', '')) {
                $input['shippingAddress']['phone'] = $phone;
            }
        }

        if ($billing_address) {
            $input['billingAddress'] = [
                "address1" => Arr::get($billing_address, "address1"),
                "city" => Arr::get($billing_address, "city"),
                "province" => Arr::get($billing_address, "province"),
                "country" => Arr::get($billing_address, "country"),
                "zip" => Arr::get($billing_address, "zip"),
                "phone" => Arr::get($billing_address, "phone"),
                'firstName' => Arr::get($billing_address, 'first_name'),
                'lastName' => Arr::get($billing_address, 'last_name'),
            ];
            if ($address2 = Arr::get($billing_address, 'address2', '')) {
                $input['billingAddress']['address2'] = $address2;
            }
        }
        return $input;
    }

    //获取订单详情
    public function getOrderDetail(Orders $order)
    {

        return [
            'order_num' => $order->last_order_name,
            'first_name' => $order->shippingAddress->first_name,
            'order_details' => ['email' => $order->email,],
            'payment_method' => [
                'pay_method_name' => $order->pay_method_name_en,
                'currency' => $order->present_currency,
                'total_price' => $order->total_present
            ],
            'shipping_address' => [
                'username' => $order->shippingAddress->name,
                'address1' => $order->shippingAddress->address1,
                'address2' => $order->shippingAddress->address2,
                'province' => $order->shippingAddress->province,
                'city' => $order->shippingAddress->city,
                'province_code' => $order->shippingAddress->province_code,
                'country' => $order->shippingAddress->country,
                'phone' => $order->shippingAddress->phone,
                'zip' => $order->shippingAddress->zip,
            ],
            'billing_address' => [
                'username' => $order->billingAddress->name,
                'address1' => $order->billingAddress->address1,
                'address2' => $order->billingAddress->address2,
                'province' => $order->billingAddress->province,
                'city' => $order->billingAddress->city,
                'province_code' => $order->billingAddress->province_code,
                'country' => $order->billingAddress->country,
                'phone' => $order->billingAddress->phone,
                'zip' => $order->billingAddress->zip,
            ],
            'shipping_method' => $order->shipping_name ?: 'Free International Shipping',
            'item_lines' => $order->items()->field(['name', 'price', 'quantity', 'image'])->select(),
            'total_price' => $order->total_present,
            'subtotal_price' => $order->subtotal_present,
            'shipping_line' => ['price' => $order->shipping_price_present, 'title' => $order->shipping_name,],

        ];
    }

    //创建贝宝快捷支付
    public function createPaypalDraftOrder(Request $request)
    {
        $order = $log = null;
        try {
            $log = Notify::saveParams(0, $request->all(), Notify::TYPE_QUICK_PAY_AND_CREATE_ORDER);
            $paypalData = $request->param('paypal', []);
            $orderNo = Arr::get($paypalData, 'id', '');
            list($addresses, $email) = PaypalService::parseAddresses($paypalData);
            $orderData = $this->formatOrderData($request);
            $orderData['email'] = $email;
            $orderData['order_no'] = $orderNo;
            $orderData['pay_id'] = $request->param('payment_id', 0);
            $orderData['is_paypal_kj'] = Orders::IS_PAYPAL_KJ_Y;
            $orderData['pay_method'] = ShopsPayment::PAY_METHOD_PAYPAL;
            $checkout = $request->param('checkout', []);
            # 存cartLineItems
            $lineItems = Arr::get($checkout, 'cart.items', []);
            $items = $this->formatCartLineItems($lineItems);
            $orderData['created_at'] = Carbon::now()->subHours(16)->toDateTimeString();
            $breakDown = PaypalService::parsePaypalBreakdown($paypalData);
            $orderData = array_merge($breakDown, $orderData);
            $payStatus = Arr::get($paypalData, 'status', '');
            $purchase_units = Arr::get($paypalData, 'purchase_units', []);
            $payment_source = Arr::get($paypalData, 'payment_source', []);
            $isGooglePay = false;
            if (array_key_exists('google_pay', $payment_source) || array_key_exists('apple_pay', $payment_source)) {
                $isGooglePay = true;
            }
            if ($purchase_units) {
                $purchaseFirst = Arr::first($purchase_units);
                $payments = $purchaseFirst['payments'];
                $capturesFirst = Arr::first($payments['captures']);
                $tradeId = $capturesFirst['id'];
                $seller_protection = Arr::get($capturesFirst, 'seller_protection', []);
                if ($seller_protection) $seller_protection_status = Arr::get($seller_protection, 'status', '-');
                $actualMoney = Arr::get($capturesFirst, 'amount.value', 0);
                // 实际扣款金额
                $orderData['actual_money'] = $actualMoney;
                $orderData['trade_no'] = $tradeId;
                if ($isGooglePay) {
                    $payStatus = Arr::get($capturesFirst, 'status', '-');
                }
                $orderData['pay_status'] = $payStatus;
            }
            $isCaptureSuccess = $payStatus == 'COMPLETED';
            $order = Orders::where('order_no', $orderNo)->findOrEmpty();
            if ($order->isEmpty()) {
                $orderData['created_at'] = Carbon::now()->subHours(16)->toDateTimeString();
                $order = Orders::create($orderData);
            } else {
                $orderData['updated_at'] = Carbon::now()->subHours(16)->toDateTimeString();
                $order->save($orderData);
            }
            # 先删除,后添加
            if ($addresses) {
                $addresses = (new Address())->fill($addresses)->getDatas();
                $order->addresses()->delete();
                $order->addresses()->saveAll($addresses);
            }
            if ($items) {
                $items = (new LineItems())->fill($items)->getDatas();
                $order->items()->delete();
                $order->items()->saveAll($items);
            }
            $log->order_id = $order->id;
            $log->save();
            Queue::push(CreateDraftOrderQueue::class,$order->id,CreateDraftOrderQueue::QUEUE_NAME);
            $draftResult = [];
            if ($isCaptureSuccess) Orders::completed($order);
            return ['order_id' => $order->id, 'draft' => $draftResult];
        } catch (\Exception $e) {
            if ($order) {
                Orders::saveExceptionMessage($order, $e->getMessage());
                $log->order_id = $order->id;
                $log->save();
            }
            throw new BusinessException($e->getMessage());
        }


    }

    public function capturePaypalByGoogleAndApple(Request $request, Orders $order)
    {
        $payLog = Notify::saveParams($order->id,['params'=>$request->param()], Notify::TYPE_PAYPAL_GOOGLE_AND_APPLE_PAY);//增加日志
        try{
            $order->email = $request->param('email','');
            $checkout_id = $request->param('checkout_id','');
            if(!$checkout_id){ #//checkoutId 不存在,创建
                $order->checkout_id = $this->createCheckoutId($request);
            }
            $order->save();
            $addresses = $this->formatAddress($request);
            $order->addresses()->delete();
            $order->addresses()->saveAll($addresses);//地址
            $paypalProcessor = new PaypalService();
            $response = $paypalProcessor->getPaypalInfo($order);
            $paypalStatus = Arr::get($response,'status','');
            if($paypalStatus == 'COMPLETED') throw new PaypalBusinessException("order has completed");
            $paypalProcessor->capture($order);
            return ["order_id"=>$order->id,"order_status"=>Orders::ORDER_STATUS_COMPLETED];
        }catch (\Exception $e){
            $logData = $payLog->params;
            $logData['error'] = $e->getMessage();
            $payLog->save();
            throw new PaypalBusinessException(CommonConstant::PAYMENT_EXCEPTION);
        }
    }

    //通过spreedly googlePay 支付
    public function captureSpreedlyByGoogleAndApple(Request $request)
    {
        $payment = ShopsPayment::payment($request->x_shop_id,ShopsPayment::PAY_METHOD_SPREEDLY);
        if($payment->isEmpty()) throw new BusinessException('spreedly payment not exist');
        //创建订单
        $order = $this->saveOrder($request);
        $order->pay_id = $payment->id;
        $order->pay_method = ShopsPayment::PAY_METHOD_SPREEDLY;
        $order->is_paypal_kj = Orders::IS_PAYPAL_KJ_Y;
        $order->paypal_method = Orders::PAYPAL_METHOD_GOOGLE;
        $checkout_id = $request->param('checkout_id','');
        if(!$checkout_id){
            $checkout_id = $this->createCheckoutId($request);
        }
        $order->checkout_id = $checkout_id;
        $order->save();
        //创建草稿订单
        Queue::push(CreateDraftOrderQueue::class,$order->id,CreateDraftOrderQueue::QUEUE_NAME);
        //获取spreedly 支付
        $spreedlyProcessor = new SpreedlyService();
        return $spreedlyProcessor->placeOrder($order,$request);//完成支付

    }



}